﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Threading;

using Paxos;

namespace SURLService
{
    class SURLServer : ReplicaStateMachine
    {
        internal SURLDictionary _dict;
        private static readonly int DictionarySize = 1024*8;
        private readonly string _httpPrefixStyle = @"http://*:{0}/";
        private readonly int _port;

        private JobManager<SURLJob> _jobManager;
        private HttpListener _listener;

        private long counter = 0;

        public SURLServer(int suggestedPort)
        {
            // initialize data.
            _dict = new SURLDictionary(DictionarySize);

            // http port: p
            // idgen port: p+100
            // acceptor port: p+200

            // add listener
            _listener = new HttpListener();
            _listener.IgnoreWriteExceptions = true;
            _listener.Start();

            List<int> usedPorts = new List<int>();
            int idlePort = 0;

            // search port, limit to 3 ants.
            for (int i = suggestedPort; i < suggestedPort + 3; ++i)
            {
                if (IsIdlePort(i))
                {
                    if (idlePort==0) idlePort = i;
                }
                else
                {
                    usedPorts.Add(i);
                }
            }
            
            // add listening port
            if (idlePort == 0)
            {
                throw new ArgumentException("Cannot find an idle port");
            }

            _port = idlePort;

            // join fixed view
            ServerView fixedView = new ServerView()
            {
                Primary = new SGUID { Port = _port },
                Servers = new List<SGUID>
                { 
                    new SGUID{Port=suggestedPort},
                    new SGUID{Port=suggestedPort+1},
                    new SGUID{Port=suggestedPort+2}
                }
            };

            this.Initilize(new SGUID { Port = idlePort }, fixedView);
            
            // try to join current view
            //if (usedPorts.Count != 0)
            //{
            //    List<SGUID> ants = usedPorts.Select(item => new SGUID { Port = item + 100 }).ToList();
            //    this.TryJoin(ants);
            //}
            //else
            //{
            //    Console.WriteLine("Cannot find a replica");
            //}
                        
            // add listener address
            _listener.Prefixes.Add(String.Format(_httpPrefixStyle, _port));
        }

        public void Start()
        {
            _jobManager = new JobManager<SURLJob>(1);
            _listener.BeginGetContext(OnRequestReceived, null);
            Console.WriteLine("...................Ready......................");
        }

        public new void Shutdown()
        {
            if (_listener != null)
            {
                _listener.Stop();
                _listener.Close();
            }

            base.Shutdown();
        }

        private bool IsIdlePort(int port)
        {
            string testPrefix = String.Format(_httpPrefixStyle, port);
            try
            {
                _listener.Prefixes.Add(testPrefix);
            }
            catch
            {
                return false;
            }

            _listener.Prefixes.Remove(testPrefix);
            return true;
        }

        private void OnRequestReceived(IAsyncResult ar)
        {
            Interlocked.Increment(ref counter);
            Console.WriteLine("Got {0} request", counter);
            HttpListenerContext listenerContext = _listener.EndGetContext(ar);
            byte[] buf = Encoding.UTF8.GetBytes(listenerContext.Request.Url.ToString());
            _jobManager.Add(new SURLJob(listenerContext, _dict, this));
            _listener.BeginGetContext(OnRequestReceived, null);            
        }

        static void Main(string[] args)
        {
            SURLServer server = new SURLServer(700);
            server.Start();            
            Console.ReadLine();

            Console.WriteLine("Dumping data...");
            server._dict.Dump();

            Console.WriteLine("Satastics: {0}", server.Counter);

            server.Shutdown();
        }
    }
}
